<?php
namespace app\websocket;

use Workerman\Connection\TcpConnection;
use Workerman\Connection\AsyncTcpConnection;

use Workerman\Protocols\Http\Chunk;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Protocols\Websocket;
use Workerman\Worker;

use Workerman\Timer;

use support\Log;
use think\facade\Db;
use support\Redis;

class TtsHandler
{
    private $redis_config;
    private $soundSavePath = '/sounds/';
    private $pre = 'http://127.0.0.1:8787';
    private $check_time_out = 3;

    public function onWorkerStart (Worker $worker)
    {
      $this->redis_config = Redis::connection("config");
    }


    public function onWebSocketConnect(TcpConnection $connection, $http_buffer)
    {
      $connection->Authentication = false;
      //创建定时器，如果连接超过10秒未认证，则关闭连接
      Timer::add($this->check_time_out, function() use ($connection){
        if($connection->Authentication == false)
        {
          $connection->send(json_encode(['status'=>400,'message'=>'Authentication failed']));
          $connection->close();
        }
      }, [], false);
    }


    public function onMessage(TcpConnection $connection, $data)
    {
      Log::info("TTS 客户端:".$data. "\n");

      $dataJson = json_decode($data,true);

      if($dataJson == null)
      {
        $connection->send(json_encode(['status'=>400,'message'=>'data format error']));
        return;
      }

      // 根据类型不同，执行不同操作接口
      if(isset($dataJson['meta_type']))
      {
        // 验证信息
        $meta_id = $dataJson['meta_id'];
        $meta_type = $dataJson['meta_type'];
        $tts_type = $dataJson['tts_type'];
        //判断参数是否存在
        if(!isset($meta_id) || !isset($meta_type) || !isset($tts_type))
        {
          $connection->send(json_encode(['status'=>400,'message'=>'meta type not found']));
          return;
        }

        $this->checkUser($connection,$meta_id,$meta_type,$tts_type);

        //音频类型。默认为audio
        $connection->AudioType = isset($dataJson["AudioType"]) ? $dataJson["AudioType"] : "audio";
        switch($connection->type)
        {
          case "aly":
            $this->bindConAly($connection);
            break;
          case "paddspeech":
            $connection->send(json_encode(['status'=>400,'message'=>'paddspeech 完善中 ']));
            $connection->close();
            break;
          default:
            $connection->send(json_encode(['status'=>400,'message'=>'tts type not found']));
            $connection->close();
            break;
        }
      }
      elseif($connection->con  && $connection->type)
      {
        switch ($connection->type) {
          case 'aly':
            $this->bindMeesageAly($dataJson,$connection);
            break;
          case 'paddspeech':
            $connection->send(json_encode(['status'=>400,'message'=>'paddspeech 完善中 ']));
            $connection->close();
            break;
          default:
            $connection->send(json_encode(['status'=>400,'message'=>'tts type not found']));
            $connection->close();
            break;
        }
      }else{
        $connection->send(json_encode(['status'=>400,'message'=>'messages not found']));
        $connection->close();
      }
    }


    public function onClose(TcpConnection $connection)
    {
      $connection->destroy();
      if(isset($connection->con))
      {
          $connection->con->close();
          $connection->con->destroy();
      }
    }

    //验证用户身份
    private function checkUser(TcpConnection &$connection,$meta_id,$meta_type,$tts_type)
    {
      switch ($meta_type) {
        case 'web':
          $res = Db::name("meta_web")
                    ->field('meta_id, role_id')
                    ->where('meta_id', $meta_id)
                    ->find();
          if($res == null)
          {
            $connection->send(json_encode(['status'=>400,'message'=>'meta not found']));
            $connection->close();
          }

          $config = getConfigByType('tts', $tts_type);
          if($config === false)
          {
            $connection->send(json_encode(['status'=>400,'message'=>'ttstype not found']));
            $connection->close();
          }

          $connection->type = $tts_type;
          $connection->appkey = $config['appkey'];
          $connection->url = $config['url'];
          break;
        case "ue":
          $res = Db::name("ue")
                    ->field('meta_id, role_id')
                    ->where('meta_id', $meta_id)
                    ->find();
          if($res == null)
          {
            $connection->send(json_encode(['status'=>400,'message'=>'meta not found']));
            $connection->close();
          }  
          
          $config = getConfigByType('tts', $tts_type);
          if($config === false)
          {
            $connection->send(json_encode(['status'=>400,'message'=>'ttstype not found']));
            $connection->close();
          }


          $connection->type = $tts_type;
          $connection->appkey = $config['appkey'];
          $connection->url = $config['url'];
          break;
        default:
          $connection->send(json_encode(['status'=>400,'message'=>'meta type not found']));
          $connection->close();
          break;
      }

      $connection->Authentication = true;
    }


    //绑定对应服务
    private function bindMeesageAly($dataJson,TcpConnection $connection)
    {
      if(!isset($dataJson['name']))
      {
        $connection->send(json_encode(['status'=>400,'message'=>'name not found']));
        $connection->close();
      };
      $actionName = $dataJson["name"];
      $message_id = generateHex(32);
      $sendData = [];
      switch($actionName)
      {
        case "start":
          $task_id = generateHex(32);
          $connection->task_id = $task_id;
          
          $enable_subtitle = false;
          if($connection->AudioType == "audio")
          {
            $enable_subtitle = true;
          }
          $sendData = [
              "header"=>[
                  "message_id" => $message_id,
                  "task_id" => $task_id,
                  "namespace" => "FlowingSpeechSynthesizer",
                  "name" =>"StartSynthesis",
                  "appkey" =>  $connection->appkey
              ],
              "payload"=>[
                  "voice" => $dataJson["voice"], // zhibing_emo zhimi_emo
                  "format" => "wav",
                  "sample_rate" => 16000,
                  "volume" =>$dataJson["volume"],
                  "speech_rate" => $dataJson["speech_rate"],
                  "pitch_rate" => $dataJson["pitch_rate"],
                  "enable_subtitle" => true,
                  "enable_phoneme_timestamp" => true
              ]
          ];
          $connection->con->send(json_encode($sendData));
          break;
          case "runSynthesis":
            $task_id =  $connection->task_id;
            $sendData = [
                "header"=>[
                    "message_id" => $message_id,
                    "task_id" => $task_id,
                    "namespace" => "FlowingSpeechSynthesizer",
                    "name" =>"RunSynthesis",
                    "appkey" =>  $connection->appkey
                ],
                "payload"=>[
                   "text" => $dataJson["text"]
                ]
            ];
            $connection->con->send(json_encode($sendData));

            $sendData2 = [
                "header"=>[
                    "message_id" => $message_id,
                    "task_id" => $task_id,
                    "namespace" => "FlowingSpeechSynthesizer",
                    "name" =>"StopSynthesis",
                    "appkey" =>  $connection->appkey
                ]
            ];
            $connection->con->send(json_encode($sendData2));
          break;
      }

    }


    private function bindConAly(TcpConnection &$connection)
    {

      $token = getToken("aly");

      $url = $connection->url."?token=".$token;

      $con = new AsyncTcpConnection($url);
      $connection->con = $con;
      $con->transport = 'ssl';


      $file = null;
      $date = null;
      $soundName = null;
      $outputFilePath = null;

      if($connection->AudioType == "audio")
      {
        //生成音频文件
        $date = date('Y-m-d'); 
        $directoryPath = public_path() . '/' . trim($this->soundSavePath, '/') . '/' . $date;
        if (!file_exists($directoryPath)) {
            mkdir($directoryPath, 0755, true); 
        }

        $uniqueString = uniqid('prefix_', true);
        $soundName = $uniqueString . '.wav';

        $outputFilePath = $directoryPath . '/' . $soundName;

        //先写入header占位符
        // $header = str_repeat("\0", 44);
        // file_put_contents($outputFilePath, $header);

        $file = fopen($outputFilePath, "ab");



      }
      

      $con->onWebSocketConnect = function(AsyncTcpConnection $con ) use ($connection)  {
        $sendData = [
            'status' => 200,
            "name" => 'connected'
        ];
        $sendData = json_encode($sendData);
        Log::info('给客护端:'.$sendData);
        $connection->send($sendData);
      };


      $con->onMessage = function(AsyncTcpConnection $con, $data) use ($connection , $file, $outputFilePath , $date  , $soundName)  {
          if(isjson($data))
          {
              // Log::info(' TTS阿里云信息：'.$data);
              $connection->websocketType = Websocket::BINARY_TYPE_BLOB;
              $dataJson = json_decode($data);
              if($dataJson->header)
              {

                  $actionName = $dataJson->header->name;
                  $sendData = [];
                  switch($actionName)
                  {
                      case "SynthesisStarted":
                          $sendData = [
                              "status" => 200,
                              "name" => "started"
                          ];
                          break;
                      case "SentenceSynthesis":
                          if($connection->AudioType != "ARRAYBUFFER")
                          {
                            $sendData = [
                              "status" => 200,
                              "name" => "synthesis",
                              "subtitles" => $dataJson->payload->subtitles
                            ];
                          }
                          break;
                      case "SynthesisCompleted":
                          if($connection->AudioType != "ARRAYBUFFER")
                          {
                              fclose($file);

                              // $audioDataSize = filesize($outputFilePath) - 44;
                              // updateWavHeader($outputFilePath, $audioDataSize);

                              $audioUrl =  $this->pre . '/' . trim($this->soundSavePath, '/'). "/" . $date . '/' . $soundName;
                              $sendData = [
                                  "status" => 200,
                                  "name" => "completed",
                                  "audioUrl" => $audioUrl
                              ];
                          }else{
                              $sendData = [
                                  "status" => 200,
                                  "name" => "completed",
                              ];
                          }
                          break;
                        default:
                          break;
                  }
                  if($sendData)
                  {
                      $connection->websocketType = Websocket::BINARY_TYPE_BLOB;
                      // Log::info('发送给前端的信息：'.json_encode($sendData));
                      $connection->send(json_encode($sendData));
                    
                      // if($sendData["name"] == "completed")
                      // {
                      //   $connection->close();
                      // }
                  }
              }
          }else{
              //判断音频类型是否是二进制，是的话直接推送前端，不是写入服务器
              if($connection->AudioType == "ARRAYBUFFER")
              {
                  $connection->websocketType = Websocket::BINARY_TYPE_ARRAYBUFFER;
                  $connection->send($data);
              }else{
                  fwrite($file, $data);
              }
          }
      };

      $con->connect();

    }



 


}


?>